package com.googlecode.maurersmarbles;

import java.util.ArrayList;
import java.util.List;

import android.graphics.Color;

public class GameLogic {

	/**
	 * The number of marbles each player has.
	 */
	public static final int MARBLES_PER_PLAYER = 4;
	/**
	 * The number of cards in a full deck.
	 */
	public static final int NUMBER_OF_CARDS_IN_FULL_DECK = 52;
	/**
	 * The number of players.
	 */
	public static final int NUMBER_OF_PLAYERS = 4;
	/**
	 * The user's player number.
	 */
	public static final int USER_PLAYER_NUMBER = 0;
	/**
	 * The marble colors.
	 */
	public static final int[] COLORS = { Color.BLUE, Color.YELLOW, Color.RED,
			Color.GREEN };

	/**
	 * Updates the given game state by applying the given card being played by
	 * the current players turn in the game state.
	 * @param gameState the game state to update
	 * @param card the card to apply being played
	 */
	public static void applyCardBeingPlayed(GameState gameState, Card card) {
		gameState.getPlayers()[gameState.getCurrentPlayersTurn()].getCards()
				.remove(card);
	}

	/**
	 * Updates the given game state by applying the given move.
	 * @param gameState the game state to update
	 * @param move the move to apply to the game state
	 */
	public static void applyMove(GameState gameState, Move move) {
		Marble marble = gameState.getPlayers()[move.getPlayerNumber()]
				.getMarble(move.getMarbleNumber());
		marble.setBoardIndex(move.getNewBoardIndex());
	}

	/**
	 * Updates the given game state by applying the given play.
	 * @param gameState the game state to update
	 * @param play the play to apply to the game state
	 */
	public static void applyPlay(GameState gameState, Play play) {
		applyCardBeingPlayed(gameState, play.getCard());
		for (Move move : play.getMoves()) {
			applyMove(gameState, move);
		}
	}

	/**
	 * Returns whether or not moving from index to newIndex crosses the given
	 * board index.
	 * @param index the current index
	 * @param newIndex the new index
	 * @param boardIndex the board index to test if it was crossed
	 * @return whether or not moving from index to newIndex crosses the given
	 *         board index
	 */
	public static boolean crossedBoardIndex(int index, int newIndex,
			int boardIndex) {
		return (index < boardIndex && newIndex > boardIndex)
				|| (index > boardIndex && newIndex < boardIndex);
	}

	/**
	 * Deals the cards to the players based on the current game state.
	 * @param gameState the game state
	 */
	public static void dealCards(GameState gameState) {

		gameState.dealer = GameLogic.getNextPlayerNumber(gameState.dealer);
		gameState.setCurrentPlayersTurn(GameLogic
				.getNextPlayerNumber(gameState.dealer));

		if (gameState.cardDeck == null || gameState.cardDeck.isEmpty()) {
			gameState.cardDeck = Util.createCardDeck();
			Util.shuffleCards(gameState.cardDeck);
		}

		int cardsPerPlayer = 4 + (gameState.cardDeck.size() == NUMBER_OF_CARDS_IN_FULL_DECK ? 1
				: 0);
		for (int i = 0; i < cardsPerPlayer; i++) {
			for (int j = 1; j <= GameLogic.NUMBER_OF_PLAYERS; j++) {
				int playerNumber = gameState.dealer + j;
				if (playerNumber >= GameLogic.NUMBER_OF_PLAYERS) {
					playerNumber -= GameLogic.NUMBER_OF_PLAYERS;
				}
				gameState.getPlayers()[playerNumber].getCards().add(
						gameState.cardDeck.remove(0));
			}
		}

	}

	/**
	 * Returns an empty start space for the given player, -1 if there isn't one.
	 * @param player the player
	 * @return an empty start space for the given player
	 */
	public static int getEmptyStartSpace(Player player) {
		int playerNumber = player.getPlayerNumber();
		for (int i = getStartMinBoardIndex(playerNumber); i <= getStartMaxBoardIndex(playerNumber); i++) {
			boolean spaceIsEmpty = true;
			for (Marble marble : player.getMarbles()) {
				if (marble.getBoardIndex() == i) {
					spaceIsEmpty = false;
					break;
				}
			}
			if (spaceIsEmpty) {
				return i;
			}
		}
		return -1;
	}

	/**
	 * Returns an empty start space for the given player number.
	 * @param gameState the game state
	 * @param playerNumber the player number
	 * @return an empty start space for the given player number
	 */
	public static int getEmptyStartSpace(GameState gameState, int playerNumber) {
		return getEmptyStartSpace(gameState.getPlayers()[playerNumber]);
	}

	/**
	 * Returns the home entry board index for the given player number.
	 * @param playerNumber the player number
	 * @return the home entry board index for the given player number
	 */
	public static int getHomeEntryBoardIndex(int playerNumber) {
		return Board.HOME_ENTRY[playerNumber];
	}

	/**
	 * Returns the home entry board index for the given player.
	 * @param player the player
	 * @return the home entry board index for the given player
	 */
	public static int getHomeEntryBoardIndex(Player player) {
		return getHomeEntryBoardIndex(player.getPlayerNumber());
	}

	/**
	 * Returns the home max board index for the given player number.
	 * @param playerNumber the player number
	 * @return the home max board index for the given player number
	 */
	public static int getHomeMaxBoardIndex(int playerNumber) {
		return Board.HOME_MAX[playerNumber];
	}

	/**
	 * Returns the home max board index for the given player.
	 * @param player the player
	 * @return the home max board index for the given player
	 */
	public static int getHomeMaxBoardIndex(Player player) {
		return getHomeMaxBoardIndex(player.getPlayerNumber());
	}

	/**
	 * Returns the home min board index for the given player number.
	 * @param playerNumber the player number
	 * @return the home min board index for the given player number
	 */
	public static int getHomeMinBoardIndex(int playerNumber) {
		return Board.HOME_MIN[playerNumber];
	}

	/**
	 * Returns the home min board index for the given player.
	 * @param player the player
	 * @return the home min board index for the given player
	 */
	public static int getHomeMinBoardIndex(Player player) {
		return getHomeMinBoardIndex(player.getPlayerNumber());
	}

	/**
	 * Returns the marble in the given space, null if there is not one.
	 * @param gameState the game state
	 * @param boardIndex the board index to check
	 * @return the marble in the given space, null if there is not one
	 */
	public static Marble getMarbleInSpace(GameState gameState, int boardIndex) {
		for (Player player : gameState.getPlayers()) {
			for (Marble marble : player.getMarbles()) {
				if (marble.getBoardIndex() == boardIndex) {
					return marble;
				}
			}
		}
		return null;
	}

	/**
	 * Returns the marbles in the start spaces.
	 * @param player the player
	 * @return the marbles in the start spaces
	 */
	public static List<Marble> getMarblesInStart(Player player) {
		List<Marble> marbles = new ArrayList<Marble>();
		for (Marble marble : player.getMarbles()) {
			if (isMarbleInStart(marble)) {
				marbles.add(marble);
			}
		}
		return marbles;
	}

	/**
	 * Returns the next player number.
	 * @param playerNumber the player number
	 * @return the next player number
	 */
	public static int getNextPlayerNumber(int playerNumber) {
		int nextPlayerNumber = playerNumber + 1;
		if (nextPlayerNumber >= NUMBER_OF_PLAYERS) {
			nextPlayerNumber = 0;
		}
		return nextPlayerNumber;
	}

	/**
	 * Returns the next player number.
	 * @param player the player
	 * @return the next player number
	 */
	public static int getNextPlayerNumber(Player player) {
		return getNextPlayerNumber(player.getPlayerNumber());
	}

	/**
	 * Returns the number of marbles in a home space.
	 * @param player the player
	 * @return the number of marbles in a home space
	 */
	public static int getNumberOfMarblesInHome(Player player) {
		int count = 0;
		for (Marble marble : player.getMarbles()) {
			if (isMarbleInHome(marble)) {
				count++;
			}
		}
		return count;
	}

	/**
	 * Returns the number of marbles that are in a start space.
	 * @param player the player
	 * @return the number of marbles that are in a start space
	 */
	public static int getNumberOfMarblesInStart(Player player) {
		int count = 0;
		for (Marble marble : player.getMarbles()) {
			if (isMarbleInStart(marble)) {
				count++;
			}
		}
		return count;
	}

	/**
	 * Returns the number of marbles that are on the board (including home).
	 * @param player the player
	 * @return the number of marbles that are on the board (including home)
	 */
	public static int getNumberOfMarblesOnBoard(Player player) {
		return MARBLES_PER_PLAYER - getNumberOfMarblesInStart(player);
	}

	/**
	 * Returns the partner for the given player number.
	 * @param gameState the game state
	 * @param playerNumber the player number
	 * @return the partner for the given player number
	 */
	public static Player getPartner(GameState gameState, int playerNumber) {
		return gameState.getPlayers()[getPartnerNumber(playerNumber)];
	}

	/**
	 * Returns the partner for the given player.
	 * @param gameState the game state
	 * @param player the player
	 * @return the partner for the given player
	 */
	public static Player getPartner(GameState gameState, Player player) {
		return getPartner(gameState, player.getPlayerNumber());
	}

	/**
	 * Returns the partner number for the given player number.
	 * @param player the player number
	 * @return the partner number for the given player number
	 */
	public static int getPartnerNumber(int playerNumber) {
		switch (playerNumber) {
			case 0:
				return 2;
			case 1:
				return 3;
			case 2:
				return 0;
			case 3:
				return 1;
		}
		return -1;
	}

	/**
	 * Returns the partner number for the given player.
	 * @param player the player
	 * @return the partner number for the given player
	 */
	public static int getPartnerNumber(Player player) {
		return getPartnerNumber(player.getPlayerNumber());
	}

	/**
	 * Returns the plays for the given player number based on the given game
	 * state.
	 * @param gameState the game state
	 * @param playerNumber the player number
	 * @return the plays for the given player number based on the given game
	 *         state
	 */
	public static List<Play> getPlays(GameState gameState, int playerNumber) {

		List<Play> plays = new ArrayList<Play>();

		Player player = gameState.getPlayers()[playerNumber];
		Player partner = gameState.getPlayers()[getPartnerNumber(playerNumber)];
		Player[] myTeam = new Player[] { player, partner };

		for (Card card : player.getCards()) {

			for (Player thisPlayer : myTeam) {

				switch (card.getId()) {

					case Card.CARD_ID_A:
						plays.addAll(getPlaysForCardKing(gameState, thisPlayer,
								card));
						plays.addAll(getPlaysForCard(gameState, thisPlayer,
								card));
						break;

					case Card.CARD_ID_7:
						plays.addAll(getPlaysForCard7(gameState, thisPlayer,
								card));
						break;

					case Card.CARD_ID_J:
						plays.addAll(getPlaysForCardJack(gameState, thisPlayer,
								card));
						break;

					case Card.CARD_ID_K:
						plays.addAll(getPlaysForCardKing(gameState, thisPlayer,
								card));
						break;

					default:
						plays.addAll(getPlaysForCard(gameState, thisPlayer,
								card));
						break;

				}

			}

		}

		// TODO Do we need to remove duplicate plays?

		return plays;

	}

	/**
	 * Returns the plays for the given player based on the given game state.
	 * @param gameState the game state
	 * @param player the player
	 * @return the plays for the given player based on the given game state
	 */
	public static List<Play> getPlays(GameState gameState, Player player) {
		return getPlays(gameState, player.getPlayerNumber());
	}

	/**
	 * Returns the plays for the given player playing the given card (does not
	 * handle A, 7, J, or K).
	 * @param gameState the game state
	 * @param player the player to get the plays for
	 * @param card the card being played (this is only needed so we can create
	 *        the Play object with the correct Card object)
	 * @return a list of plays for the given player playing the given card
	 */
	public static List<Play> getPlaysForCard(GameState gameState,
			Player player, Card card) {

		List<Play> plays = new ArrayList<Play>();

		// do a quick check to see if we can save some work
		if (card.getId() == Card.CARD_ID_4) {
			if (getNumberOfMarblesOnBoard(player)
					- getNumberOfMarblesInHome(player) <= 0) {
				return plays;
			}
		}
		else if (!hasMarbleOnBoard(player)) {
			return plays;
		}

		// check each marble
		for (Marble marble : player.getMarbles()) {

			int index = marble.getBoardIndex();

			// check to see if we are in a home space
			if (isMarbleInHome(marble)) {

				// cannot play a 4 or a card that goes beyond our home spaces
				if (card.getValue() < 0
						|| (index + card.getValue() > getHomeMaxBoardIndex(player))) {
					continue;
				}

				boolean validMove = true;
				for (int j = index + 1; j <= index + card.getValue(); j++) {
					if (isPlayerMarbleInSpace(player, j)) {
						validMove = false;
						break;
					}
				}
				if (validMove) {
					Play play = new Play(card);
					play.addMove(new Move(player, marble, index
							+ card.getValue()));
					plays.add(play);
				}

				continue;

			}

			// check to make sure this marble is on the board
			if (!isMarbleOnBoardInPlay(marble)) {
				continue;
			}

			boolean wrapped = false;
			int newIndex = index + card.getValue();
			if (newIndex >= Board.BOARD_SPACES) {
				newIndex = newIndex - Board.BOARD_SPACES;
				wrapped = true;
			}
			else if (newIndex < 0) {
				newIndex = newIndex + Board.BOARD_SPACES;
				wrapped = true;
			}

			// check to see if we passed our home entry space
			if (index <= getHomeEntryBoardIndex(player)
					&& index + card.getValue() > getHomeEntryBoardIndex(player)) {

				int diff = index + card.getValue()
						- getHomeEntryBoardIndex(player);

				// cannot move more than 4 positions in home spaces
				if (diff > 4) {
					continue;
				}

				boolean validMove = true;
				for (int j = 0; j < diff; j++) {
					if (isPlayerMarbleInSpace(player,
							getHomeMinBoardIndex(player) + j)) {
						validMove = false;
						break;
					}
				}
				if (!validMove) {
					continue;
				}

				// this is a valid play, add it to the list
				Play play = new Play(card);
				play.addMove(new Move(player, marble,
						getHomeMinBoardIndex(player) + (diff - 1)));
				plays.add(play);

				continue;

			}

			// check to see if we landed on our marble
			if (isOurMarbleInSpace(gameState, player, newIndex)) {
				continue;
			}

			// check to see if we landed on a safe marble
			// TODO Can we simplify this when we check to see if we passed a
			// safe marble?
			boolean validMove = true;
			for (Player tmpPlayer : gameState.getPlayers()) {
				// TODO Need to check for non safe marble in safe space
				if (newIndex == getSafeBoardIndex(tmpPlayer)
						&& hasMarbleInSafe(tmpPlayer)) {
					validMove = false;
					break;
				}
			}
			if (!validMove) {
				continue;
			}

			// check to see if we passed a safe marble
			if (!isMarbleInSafe(marble)) {

				validMove = true;
				for (Player tmpPlayer : gameState.getPlayers()) {

					// check to see if the player has a marble in the safe space
					if (hasMarbleInSafe(tmpPlayer)) {

						// the board resets on player 0's safe space
						if (tmpPlayer.getPlayerNumber() != 0) {
							if (crossedBoardIndex(index,
									index + card.getValue(),
									getSafeBoardIndex(tmpPlayer))) {
								validMove = false;
								break;
							}
						}
						else if (wrapped) {
							validMove = false;
							break;
						}

					}

				}
				if (!validMove) {
					continue;
				}

			}

			// all tests passed, add this play
			Play play = new Play(card);
			play.addMove(new Move(player, marble, newIndex));

			// check to see if there is another marble in the new index
			Marble previousMarble = getMarbleInSpace(gameState, newIndex);
			if (previousMarble != null) {
				play.addMove(new Move(previousMarble.getPlayerNumber(),
						previousMarble.getMarbleNumber(), getEmptyStartSpace(
								gameState, previousMarble.getPlayerNumber())));
			}

			plays.add(play);

		}

		return plays;

	}

	/**
	 * Returns the plays for the given player playing a 7.
	 * @param gameState the game state
	 * @param player the player to get the plays for
	 * @param card the card being played (this is only needed so we can create
	 *        the Play object with the correct Card object)
	 * @return a list of plays for the given player playing a 7
	 */
	public static List<Play> getPlaysForCard7(GameState gameState,
			Player player, Card card) {

		List<Play> plays = new ArrayList<Play>();

		// check to make sure we have a marble on the board
		if (!hasMarbleOnBoard(player)) {
			return plays;
		}

		Card tmpCard1;
		Card tmpCard2;
		Player[] myTeam = new Player[] { player, getPartner(gameState, player) };

		// first, get the plays for moving 1 marble 7 spaces
		tmpCard1 = new Card(Card.CARD_ID_SPLIT, card.getSuit(), 7);
		for (Play tmpPlay : getPlaysForCard(gameState, player, tmpCard1)) {
			// need to create a new play with the correct card
			Play play = new Play(card);
			play.addMoves(tmpPlay.getMoves());
			plays.add(play);
		}

		// second, get the plays for splitting 7 between 2 marbles (my own
		// and/or my partners)
		for (int value1 = 1; value1 <= 6; value1++) {

			int value2 = 7 - value1;
			tmpCard1 = new Card(Card.CARD_ID_SPLIT, card.getSuit(), value1);
			tmpCard2 = new Card(Card.CARD_ID_SPLIT, card.getSuit(), value2);

			List<Play> tmpPlays1 = getPlaysForCard(gameState, player, tmpCard1);
			for (Play play1 : tmpPlays1) {

				// create a new game state that we can modify
				GameState tmpGameState = new GameState(gameState);

				/**
				 * Apply the first play to the temp game state. Note this
				 * function will try to remove the tmp card that we created but
				 * we don't care for this purpose. If this turns out to be a
				 * valid move, the play that gets created is updated later to
				 * the correct card.
				 */
				applyPlay(tmpGameState, play1);

				for (Player thisPlayer : myTeam) {

					List<Play> tmpPlays2 = getPlaysForCard(tmpGameState,
							thisPlayer, tmpCard2);
					for (Play play2 : tmpPlays2) {

						/**
						 * Check to make sure we aren't trying to move the same
						 * marble. It is possible for play1 or play2 to have
						 * more than 1 move but since we are executing a 1-6
						 * move, the first move will always be our marble being
						 * moved. The only way there will be more than 1 move
						 * for play1 or play2 is if we land on another marble.
						 */
						if (Move.isSamePlayerAndMarble(play1.getMoves().get(0),
								play2.getMoves().get(0))) {
							continue;
						}

						Play play = new Play(card);
						play.addMoves(play1.getMoves());
						play.addMoves(play2.getMoves());
						plays.add(play);

					}

				}

			}

		}

		return plays;

	}

	/**
	 * Returns the plays for the given player playing a Jack.
	 * @param gameState the game state
	 * @param player the player to get the plays for
	 * @param card the card being played (this is only needed so we can create
	 *        the Play object with the correct Card object)
	 * @return a list of plays for the given player playing a Jack
	 */
	public static List<Play> getPlaysForCardJack(GameState gameState,
			Player player, Card card) {

		List<Play> plays = new ArrayList<Play>();

		for (Marble myMarble : player.getMarbles()) {

			// make sure this marble is on the board in play
			if (!isMarbleOnBoardInPlay(myMarble)) {
				continue;
			}

			for (Player otherPlayer : gameState.getPlayers()) {

				// cannot swap marble with our own
				if (otherPlayer == player) {
					continue;
				}

				for (Marble theirMarble : otherPlayer.getMarbles()) {

					// marble must be on the board in play
					if (!isMarbleOnBoardInPlay(theirMarble)) {
						continue;
					}

					// cannot swap safe marble unless it is our partners
					if (!isMarbleInSafe(theirMarble)
							|| otherPlayer.getPlayerNumber() == getPartnerNumber(player)) {
						Play play = new Play(card);
						play.addMove(new Move(player, myMarble, theirMarble
								.getBoardIndex()));
						play.addMove(new Move(otherPlayer, theirMarble,
								myMarble.getBoardIndex()));
						plays.add(play);
					}

				}

			}

		}

		return plays;

	}

	/**
	 * Returns the plays for the given player playing a King.
	 * @param gameState the game state
	 * @param player the player to get the plays for
	 * @param card the card being played (this is only needed so we can create
	 *        the Play object with the correct Card object)
	 * @return a list of plays for the given player playing a King
	 */
	public static List<Play> getPlaysForCardKing(GameState gameState,
			Player player, Card card) {

		List<Play> plays = new ArrayList<Play>();

		if (hasMarbleInStart(player)
				&& !hasMarbleInSafe(player)
				&& !hasMarbleInSpace(getPartner(gameState, player),
						getSafeBoardIndex(player))) {

			for (Marble marble : getMarblesInStart(player)) {

				// create the play and add the move
				Play play = new Play(card);
				play.addMove(new Move(player, marble, getSafeBoardIndex(player)));

				// check to see if there is another marble in our safe spot
				Marble previousMarble = getMarbleInSpace(gameState,
						getSafeBoardIndex(player));
				if (previousMarble != null) {
					play.addMove(new Move(previousMarble.getPlayerNumber(),
							previousMarble.getMarbleNumber(),
							getEmptyStartSpace(gameState,
									previousMarble.getPlayerNumber())));
				}

				plays.add(play);

			}

		}

		return plays;

	}

	/**
	 * Returns the safe board index for the given player number.
	 * @param playerNumber the player number
	 * @return the safe board index for the given player number
	 */
	public static int getSafeBoardIndex(int playerNumber) {
		return Board.SAFE[playerNumber];
	}

	/**
	 * Returns the safe board index for the given player.
	 * @param player the player
	 * @return the safe board index for the given player
	 */
	public static int getSafeBoardIndex(Player player) {
		return getSafeBoardIndex(player.getPlayerNumber());
	}

	/**
	 * Returns the start max board index for the given player number.
	 * @param playerNumber the player number
	 * @return the start max board index for the given player number
	 */
	public static int getStartMaxBoardIndex(int playerNumber) {
		return Board.START_MAX[playerNumber];
	}

	/**
	 * Returns the start max board index for the given player.
	 * @param player the player
	 * @return the start max board index for the given player
	 */
	public static int getStartMaxBoardIndex(Player player) {
		return getStartMaxBoardIndex(player.getPlayerNumber());
	}

	/**
	 * Returns the start min board index for the given player number.
	 * @param playerNumber the player number
	 * @return the start min board index for the given player number
	 */
	public static int getStartMinBoardIndex(int playerNumber) {
		return Board.START_MIN[playerNumber];
	}

	/**
	 * Returns the start min board index for the given player.
	 * @param player the player
	 * @return the start min board index for the given player
	 */
	public static int getStartMinBoardIndex(Player player) {
		return getStartMinBoardIndex(player.getPlayerNumber());
	}

	/**
	 * Returns whether or not the given player has a marble in the safe space.
	 * @param player the player
	 * @return whether or not the given player has a marble in the safe space
	 */
	public static boolean hasMarbleInSafe(Player player) {
		// TODO Need to account for non safe marble in safe space
		for (Marble marble : player.getMarbles()) {
			if (marble.getBoardIndex() == getSafeBoardIndex(player)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Returns whether or not the given player has a marble in the given space.
	 * @param player the player to check
	 * @param boardIndex the board index to check
	 * @return whether or not the given player has a marble in he given space
	 */
	public static boolean hasMarbleInSpace(Player player, int boardIndex) {
		for (Marble marble : player.getMarbles()) {
			if (marble.getBoardIndex() == boardIndex) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Returns whether or not there is a marble in a start space.
	 * @param player the player
	 * @return whether or not there is a marble in a start space
	 */
	public static boolean hasMarbleInStart(Player player) {
		return getNumberOfMarblesInStart(player) > 0;
	}

	/**
	 * Returns whether or not there is a marble on the board (including home).
	 * @param player the player
	 * @return whether or not there is a marble on the board (including home)
	 */
	public static boolean hasMarbleOnBoard(Player player) {
		return getNumberOfMarblesOnBoard(player) > 0;
	}

	/**
	 * Returns whether or not the game is over.
	 * @param gameState the game state
	 * @return whether or not the game is over
	 */
	public static boolean isGameOver(GameState gameState) {
		for (int i = 0; i < NUMBER_OF_PLAYERS / 2; i++) {
			if (getNumberOfMarblesInHome(gameState.players[i]) == MARBLES_PER_PLAYER
					&& getNumberOfMarblesInHome(gameState.players[getPartnerNumber(i)]) == MARBLES_PER_PLAYER) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Returns whether or not the given marble is in a home space.
	 * @param marble the marble to check
	 * @return whether or not the given marble is in a home space
	 */
	public static boolean isMarbleInHome(Marble marble) {
		if (marble.getBoardIndex() >= getHomeMinBoardIndex(marble
				.getPlayerNumber())
				&& marble.getBoardIndex() <= getHomeMaxBoardIndex(marble
						.getPlayerNumber())) {
			return true;
		}
		return false;
	}

	/**
	 * Returns whether or not the given marble is in the safe space.
	 * @param marble the marble
	 * @return whether or not the given marble is in the safe space
	 */
	public static boolean isMarbleInSafe(Marble marble) {
		return marble.getBoardIndex() == getSafeBoardIndex(marble
				.getPlayerNumber());
	}

	/**
	 * Returns whether or not the given marble is in a start space.
	 * @param marble the marble to check
	 * @return whether or not the given marble is in a start space
	 */
	public static boolean isMarbleInStart(Marble marble) {
		if (marble.getBoardIndex() >= getStartMinBoardIndex(marble
				.getPlayerNumber())
				&& marble.getBoardIndex() <= getStartMaxBoardIndex(marble
						.getPlayerNumber())) {
			return true;
		}
		return false;
	}

	/**
	 * Returns whether or not the given marble is on the board and in play (on
	 * board but not in home).
	 * @param marble the marble
	 * @return whether or not the given marble is on the board and in play
	 */
	public static boolean isMarbleOnBoardInPlay(Marble marble) {
		return marble.getBoardIndex() < Board.BOARD_SPACES;
	}

	/**
	 * Returns whether or not our marble is in the given space.
	 * @param gameState the game state
	 * @param player the player
	 * @param boardIndex the board index
	 * @return whether or not our marble is in the given space
	 */
	public static boolean isOurMarbleInSpace(GameState gameState,
			Player player, int boardIndex) {
		return isPlayerMarbleInSpace(player, boardIndex)
				|| isPlayerMarbleInSpace(getPartner(gameState, player),
						boardIndex);
	}

	/**
	 * Returns whether or not the given player's marble is in the given space.
	 * @param player the player
	 * @param boardIndex the board index to check
	 * @return whether or not the given player's marble is in the given space
	 */
	public static boolean isPlayerMarbleInSpace(Player player, int boardIndex) {
		for (Marble marble : player.getMarbles()) {
			if (marble.getBoardIndex() == boardIndex) {
				return true;
			}
		}
		return false;
	}

}
